﻿using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;

namespace Devonline.AspNetCore;

/// <summary>
/// 数据带附件及附加信息的基础数据服务
/// 业务数据的附件全都在名为 Attachments 的集合中, 根据 BusinessType 字段区分用途
/// </summary>
/// <typeparam name="TDbContext">数据库上下文</typeparam>
/// <typeparam name="TEntitySet">业务数据类型</typeparam>
/// <typeparam name="TAttachment">附件数据类型</typeparam>
/// <typeparam name="TAdditional">附加信息数据类型</typeparam>
/// <typeparam name="TKey">主键类型</typeparam>
public class DataWithAttachmentAndAdditionalService<TDbContext, TEntitySet, TAttachment, TAdditional, TKey> :
    DataWithAttachmentService<TDbContext, TEntitySet, TAttachment, TKey>,
    IDataWithAttachmentAndAdditionalService<TDbContext, TEntitySet, TAttachment, TAdditional, TKey>,
    IDataWithAttachmentService<TDbContext, TEntitySet, TAttachment, TKey>,
    IDataWithAdditionalService<TDbContext, TEntitySet, TAdditional, TKey>,
    IDataService<TDbContext, TEntitySet, TKey>,
    IDataAttachmentService<TDbContext, TEntitySet, TAttachment, TKey>,
    IDataAdditionalService<TDbContext, TEntitySet, TAdditional, TKey>,
    IAttachmentService<TDbContext, TAttachment, TKey>
    where TDbContext : DbContext
    where TEntitySet : class, IEntitySet<TKey>
    where TAttachment : class, IAttachment<TKey>
    where TAdditional : class, IAdditional<TKey>
    where TKey : IConvertible
{
    protected readonly IDataWithAdditionalService<TDbContext, TEntitySet, TAdditional, TKey> _dataWithAdditionalService;
    public DataWithAttachmentAndAdditionalService(
        TDbContext context,
        ILogger<DataWithAttachmentAndAdditionalService<TDbContext, TEntitySet, TAttachment, TAdditional, TKey>> logger,
        IDistributedCache cache,
        IHttpContextAccessor httpContextAccessor,
        AppSetting appSetting,
        IAttachmentService<TDbContext, TAttachment, TKey> attachmentService,
        IDataWithAdditionalService<TDbContext, TEntitySet, TAdditional, TKey> dataWithAdditionalService
        ) : base(context, logger, cache, httpContextAccessor, appSetting, attachmentService) => _dataWithAdditionalService = dataWithAdditionalService;

    #region 实现自 IDataWithAdditionalService 接口的方法
    /// <summary>
    /// 新增业务数据的附加信息
    /// </summary>
    /// <param name="entitySet">业务数据</param>
    /// <param name="additionals">附加数据集合</param>
    /// <returns></returns>
    public virtual Task AddAdditionalsAsync(TEntitySet entitySet, IEnumerable<TAdditional>? additionals = default) => _dataWithAdditionalService.AddAdditionalsAsync(entitySet, additionals);
    /// <summary>
    /// 更新业务数据的附加信息
    /// </summary>
    /// <param name="entitySet">业务数据</param>
    /// <param name="additionals">附加数据集合</param>
    /// <param name="isLogical">是否逻辑操作, 默认不是</param>
    /// <returns></returns>
    public virtual Task UpdateAdditionalsAsync(TEntitySet entitySet, IEnumerable<TAdditional>? additionals = default, bool isLogical = false) => _dataWithAdditionalService.UpdateAdditionalsAsync(entitySet, additionals, isLogical);
    /// <summary>
    /// 删除业务数据的附加信息
    /// </summary>
    /// <param name="id">业务数据主键</param>
    /// <param name="additionals">附加数据集合</param>
    /// <param name="isLogical">是否逻辑操作, 默认不是</param>
    /// <returns></returns>
    public virtual Task DeleteAdditionalsAsync(TKey id, bool isLogical = false) => _dataWithAdditionalService.DeleteAdditionalsAsync(id, isLogical);
    #endregion

    #region 继承自 DataService 的方法
    /// <summary>
    /// 此重载新增方法会同时将当前业务数据的附加信息及附件一并新增
    /// </summary>
    /// <param name="entitySet">业务数据</param>
    /// <param name="context">数据操作上下文</param>
    protected override async Task InternalAddAsync(TEntitySet entitySet, DataServiceContext? context = default)
    {
        _logger.LogInformation("The user {user} will add {typeName} and it's additionals, attachments, the content is: " + entitySet.ToJsonString(), UserName, _typeName);

        await base.InternalAddAsync(entitySet, context);
        await AddAdditionalsAsync(entitySet, entitySet.GetCollectionMember<TEntitySet, TAdditional>());
    }
    /// <summary>
    /// 重载的更新方法会同时将当前业务数据的附加信息及附件一并更新
    /// </summary>
    /// <param name="entitySet">业务数据</param>
    /// <param name="context">数据操作上下文</param>
    /// <returns></returns>
    protected override async Task InternalUpdateAsync(TEntitySet entitySet, DataServiceContext? context = default)
    {
        var isLogical = context?.IsLogical ?? false;
        _logger.LogInformation("The user {user} will {isLogical} update {typeName} and it's additionals, attachments, the content is: " + entitySet.ToJsonString(), UserName, GetLogicalString(isLogical), _typeName);

        await base.InternalUpdateAsync(entitySet, context);
        await UpdateAdditionalsAsync(entitySet, entitySet.GetCollectionMember<TEntitySet, TAdditional>(), isLogical);
    }
    /// <summary>
    /// 重载的删除方法会同时将当前业务数据的附加信息及附件一并删除
    /// </summary>
    /// <param name="id">业务数据主键</param>
    /// <param name="context">数据操作上下文</param>
    /// <returns></returns>
    protected override async Task InternalDeleteAsync(TKey id, DataServiceContext? context = default)
    {
        var isLogical = context?.IsLogical ?? false;
        _logger.LogInformation("The user {user} will {isLogical} delete {typeName} and it's additionals, attachments, the content id is: " + id, UserName, GetLogicalString(isLogical), _typeName);

        await base.InternalDeleteAsync(id, context);
        await DeleteAdditionalsAsync(id, isLogical);
    }
    #endregion
}

/// <summary>
/// 数据带附件及附加信息的基础数据服务
/// 字符串作为主键的默认实现
/// 业务数据的附件全都在名为 Attachments 的集合中, 根据 BusinessType 字段区分用途
/// </summary>
/// <typeparam name="TDbContext">数据库上下文</typeparam>
/// <typeparam name="TEntitySet">业务数据类型</typeparam>
/// <typeparam name="TAdditional">附加信息数据类型</typeparam>
public class DataWithAttachmentAndAdditionalService<TDbContext, TEntitySet, TAdditional> :
    DataWithAttachmentAndAdditionalService<TDbContext, TEntitySet, Attachment, TAdditional, string>,
    IDataWithAttachmentAndAdditionalService<TDbContext, TEntitySet, TAdditional>,
    IDataWithAttachmentService<TDbContext, TEntitySet>,
    IDataWithAdditionalService<TDbContext, TEntitySet, TAdditional>,
    IDataService<TDbContext, TEntitySet>,
    IDataAttachmentService<TDbContext, TEntitySet>,
    IDataAdditionalService<TDbContext, TEntitySet, TAdditional>
    where TDbContext : DbContext
    where TEntitySet : class, IEntitySet
    where TAdditional : class, IAdditional
{
    public DataWithAttachmentAndAdditionalService(
        TDbContext context,
        ILogger<DataWithAttachmentAndAdditionalService<TDbContext, TEntitySet, TAdditional>> logger,
        IDistributedCache cache,
        IHttpContextAccessor httpContextAccessor,
        AppSetting appSetting,
        IAttachmentService<TDbContext> attachmentService,
        IDataWithAdditionalService<TDbContext, TEntitySet, TAdditional> dataWithAdditionalService
        ) : base(context, logger, cache, httpContextAccessor, appSetting, attachmentService, dataWithAdditionalService) { }
}